#include "pch.hpp"
#include "NumberWidget.hpp"

using namespace Engine;
using namespace Engine::Edit;




NumberWidget::NumberWidget(QWidget *parent)
	: QWidget(parent)
	, edit(nullptr)

	, m_metrics(m_font)

	, stopMarker(":/icons/stop")
	, currentMarker("images/next.png")
	, bugMarker(":/icons/bug")

	, m_hasBreakpoints(true)
{
	verify(stopMarker.isNull() == false);
	verify(bugMarker.isNull() == false);

	setBackgroundRole(QPalette::Base);
	setAutoFillBackground(true);
}
///////////////////////////////////////////////////////////////////////////////////////////////////

NumberWidget::~NumberWidget()
{}
///////////////////////////////////////////////////////////////////////////////////////////////////




void NumberWidget::setHasBreakpoints(bool enabled)
{
	m_hasBreakpoints = enabled;
	documentChanged();
}
///////////////////////////////////////////////////////////////////////////////////////////////////

void NumberWidget::setCurrentLine(int lineno)
{
	currentLine = lineno;
}
///////////////////////////////////////////////////////////////////////////////////////////////////

void NumberWidget::addBreakpoint(int line)
{
	m_breakpoints.insert(line, QRect());

	update();
}
///////////////////////////////////////////////////////////////////////////////////////////////////

void NumberWidget::removeBreakpoint(int line)
{
	m_breakpoints.remove(line);

	update();
}
///////////////////////////////////////////////////////////////////////////////////////////////////

void NumberWidget::setBugLine(int lineno)
{
	bugLine = lineno;
}
///////////////////////////////////////////////////////////////////////////////////////////////////

void NumberWidget::setFont(QFont font)
{
	m_font    = font;
	m_metrics = QFontMetrics(font);
}
///////////////////////////////////////////////////////////////////////////////////////////////////

void NumberWidget::setTextEdit(QTextEdit* edit)
{
	this->edit = edit;
	connect(edit->document()->documentLayout(), SIGNAL(update(const QRectF &)), this, SLOT(documentChanged()));
	connect(edit->verticalScrollBar(), SIGNAL(valueChanged(int)), this, SLOT(update()));
}
///////////////////////////////////////////////////////////////////////////////////////////////////

void NumberWidget::paintEvent(QPaintEvent*)
{
	if(edit)
	{
		auto layout      = edit->document()->documentLayout();
		int contentsY    = edit->verticalScrollBar()->value();
		qreal pageBottom = contentsY + edit->viewport()->height();
		const auto fm    = fontMetrics();
		const int ascent = fontMetrics().ascent() + 1; // height = ascent + descent + 1
		int lineCount    = 1;

		QPainter p(this);

		//bugRect = QRect();
		//stopRect = QRect();
		currentRect = QRect();

		for(QTextBlock block = edit->document()->begin(); block.isValid(); block = block.next(), ++lineCount)
		{
			const QRectF boundingRect = layout->blockBoundingRect(block);

			QPointF position = boundingRect.topLeft();
			if(position.y() + boundingRect.height() < contentsY)
				continue;
			if(position.y() > pageBottom)
				break;

			const QString txt = QString::number(lineCount);
			p.drawText(width() - fm.width(txt), qRound(position.y()) - contentsY + ascent, txt);

			// Bug marker
			if(bugLine == lineCount)
			{
				p.drawPixmap(1, qRound(position.y()) - contentsY, bugMarker);
				//bugRect = QRect(1, qRound(position.y()) - contentsY, bugMarker.width(), bugMarker.height());
			}

			// Breakpoint marker
			auto it = m_breakpoints.find(lineCount);
			if(it != m_breakpoints.end())
			{
				*it = QRect(19, qRound(position.y()) - contentsY, stopMarker.width(), stopMarker.height());
				p.drawPixmap(*it, stopMarker);
			}

			// Current line marker
			if(currentLine == lineCount)
			{
				p.drawPixmap(19, qRound(position.y()) - contentsY, currentMarker);
				currentRect = QRect(19, qRound(position.y()) - contentsY, currentMarker.width(), currentMarker.height());
			}
		}
	}
}
///////////////////////////////////////////////////////////////////////////////////////////////////

void NumberWidget::mouseReleaseEvent(QMouseEvent* event)
{
	// If this hits a breakpoint that has already been hit, then
	// it's removed, otherwise we add a breakpoint
	for(auto it = m_breakpoints.begin(); it != m_breakpoints.end(); ++it)
	{
		if(it->contains(event->pos()))
		{
			m_breakpoints.erase(it);
			update();

			return;
		}
	}

	if(edit)
	{
		auto layout      = edit->document()->documentLayout();
		int contentsY    = edit->verticalScrollBar()->value();
		qreal pageBottom = contentsY + edit->viewport()->height();
		int lineCount    = 1;

		for(QTextBlock block = edit->document()->begin(); block.isValid(); block = block.next(), ++lineCount)
		{
			const QRectF boundingRect = layout->blockBoundingRect(block);

			QPointF position = boundingRect.topLeft();
			if(position.y() + boundingRect.height() < contentsY)
				continue;
			if(position.y() > pageBottom)
				break;

			if(boundingRect.top() <= event->pos().y() && boundingRect.bottom() >= event->pos().y())
			{
				m_breakpoints.insert(lineCount, QRect());
				update();

				break;
			}
		}
	}
}
///////////////////////////////////////////////////////////////////////////////////////////////////

bool NumberWidget::event(QEvent *event)
{
	if(event->type() == QEvent::ToolTip)
	{
		QHelpEvent *helpEvent = static_cast<QHelpEvent *>(event);

		/*if(stopRect.contains(helpEvent->pos()))
		{
			QToolTip::showText(helpEvent->globalPos(), "Stop Here");
		}
		else*/

		if(currentRect.contains(helpEvent->pos()))
		{
			QToolTip::showText(helpEvent->globalPos(), "Current Line");
		}

		foreach(const auto& rect, m_breakpoints)
		{
			if(rect.contains(helpEvent->pos()))
				QToolTip::showText(helpEvent->globalPos(), "Breakpoint");
		}
	}

	return QWidget::event(event);
}
///////////////////////////////////////////////////////////////////////////////////////////////////

void NumberWidget::documentChanged()
{
	int width = 0;
	int lineCount = edit->document()->blockCount();

	width += edit->document()->blockCount();
	width += m_metrics.width(QString::number(lineCount));

	if(m_hasBreakpoints)
	{
		width += stopMarker.width();
		width += bugMarker.width();
	}

	// Make room for 4 digits, the breakpoint icon and the bug icon
	setFixedWidth(width);

	// Draw new / old linenumbers
	update();
}
///////////////////////////////////////////////////////////////////////////////////////////////////
